#include "OK.h"
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>
#include <list>
#include <ctime>
using namespace std;

int main(int argc, char *argv[])
{
    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
    
    try {
		char* input=(argc>1?argv[1]:"data.dat");
        char* output=(argc>2?argv[2]:"result.out");
		int minSup = (argc>3?atoi(argv[3]):0);
		bool asChars = (argc>4 && argv[4][0]=='c');
		cout<<"asChars: "<<asChars<<endl;
		DataStructure* data = loadDataSet(input);
        data->printSummary(std::cout);
		cout<<endl;
		std::ofstream fc(output);
		OK(data,&fc,minSup,NULL,asChars);
		fc.close();
        delete data;
    }
    catch (std::exception e) {
        e.what();
    }

    _CrtDumpMemoryLeaks();
    system("PAUSE");
    return 0;
}

void OK(DataStructure *ds, std::ofstream *fc, unsigned int minSup, bool (*sort_f)(const OAPair &e1, const OAPair &e2), bool printAsChars){
	long startTime = clock();
	if(ds->getAllObjects()->data.size()<2){
		cout<<"One object only"<<endl;
		return;
	}

	ObjectSet* commonObjects=NULL;
	FormalContext* initialContext = new FormalContext();

	vector<const OAPair * const> *frequentClosedItemsets;
	for (std::set<Attribute>::const_iterator i = ds->getAllAtributes()->data.begin(); i != ds->getAllAtributes()->data.end(); i++){
		//if the objects exist for the atrribute in a number no less than minSup
		ObjectSet* support = ds->getObjects(*i);
		if(support->data.size() >= minSup){
			//////initializing context as side-effect///////////
			OAPair tmpPair = std::make_pair( support  , new AttributeSet());
			tmpPair.second->data.insert(Attribute(*i));
			initialContext->data.push_back(tmpPair);
			////////////////////////////////////////////////////
			if(commonObjects==NULL){
				//first one
				commonObjects=new ObjectSet(*(ds->getObjects(*i)));
			}else{
				//remove non-matching ones
				for(std::set<Object>::iterator stored = commonObjects->data.begin();
					stored != commonObjects->data.end();){
					if(ds->getObjects(*i)->data.find(*stored)==ds->getObjects(*i)->data.end()){
						std::set<Object>::iterator toRemove = stored++;
						commonObjects->data.erase(toRemove);
						//perhaps all 've been removed
						if(commonObjects->data.size()==0){
							break;
						}
						continue;
					}
					stored++;
				}
			}
		}else{
			if(commonObjects!=NULL){
				commonObjects->data.erase(commonObjects->data.begin(), commonObjects->data.end());
			}
		}
	}

	if(commonObjects==NULL){
		cout<<endl<<"Not a single item has been found with sufficient support. Aborting"<<endl;
		cleanUpFormalContext(initialContext, false, true);
		return;
	}else{
		frequentClosedItemsets = new vector<const OAPair* const>();
		if(commonObjects->data.size()>0){
			OAPair *found = new OAPair();
			found->first=commonObjects;
			found->second=ds->getAllAtributes();
			insertFrequentItemset(found, frequentClosedItemsets);
			minSup=commonObjects->data.size()+1;
		}else{
			if(minSup==0){
				++minSup;
			}
			frequentClosedItemsets->push_back(new pair<ObjectSet*, AttributeSet*>(new ObjectSet(), new AttributeSet(*ds->getAllAtributes())));
		}
		delete commonObjects;
	}

	if(ds->getAllObjects()->data.size()>=minSup){
		ObjectSet *allObjectSet = new ObjectSet(*(ds->getAllObjects()));
		AttributeSet *initialAttributeSet = new AttributeSet();
		AttributeSet *attrSet4oa = new AttributeSet();

		OAPair* oa = new pair<ObjectSet*, AttributeSet*>(allObjectSet, attrSet4oa);
		FormalContext *localContext = OKLocalContext(*oa, initialContext);
		Closure(*initialAttributeSet, *oa, *localContext);
		insertFrequentItemset(oa, frequentClosedItemsets);
		OKExtend(initialAttributeSet, localContext, sort_f, minSup, frequentClosedItemsets);
		//printFrequentClosedItemsets(frequentClosedItemsets, printAsChars);
		printFrequentClosedItemsets(frequentClosedItemsets, printAsChars, *fc);
		cleanUpFormalContext(localContext, true, true);
		delete initialAttributeSet;
		cleanUpOAPair(oa);
		cleanUpFormalContext(initialContext, false, true);
	}
	cleanUpFrequentClosedItemsets(frequentClosedItemsets);
	float exTime=(clock()-startTime)/(float)1000;
	*fc<<"OK completion time: "<<exTime<<" seconds"<<endl;
	cout<<"OK completion time: "<<exTime<<" seconds"<<endl;
}


void OKExtend(AttributeSet * prefix, FormalContext *P, bool (*sort_f)(const OAPair &e1, const OAPair &e2), unsigned int minSup, vector<const OAPair* const> *frequentClosedItemsets){
	Order((*P),sort_f);
	if(!checkOrder(P)){
		cout<<"Context in wrong order"<<endl;
		system("PAUSE");
	}
	for(vector<OAPair>::iterator iter = P->data.begin()+P->processedNo; iter!=P->data.end() && iter!=--(P->data.end()); ++iter){
		iter->second->processed=true;
		if(isClosable(*iter, *P)){
			FormalContext *poa = OKLocalContextMinSup(*iter, P, minSup);
			Closure(*prefix, *iter, *poa);
			insertFrequentItemset(&OAPair(*iter), frequentClosedItemsets);
			if(poa->data.size()>poa->processedNo){
				OKExtend(iter->second, poa, sort_f, minSup, frequentClosedItemsets);
			}
			cleanUpFormalContext(poa, true, true);
		}
	}
	if(P->data.size()>0 && isClosable(*(P->data.rbegin()),*P)){
		OAPair *oapair = new pair<ObjectSet*, AttributeSet*>(new ObjectSet(),new AttributeSet());
		oapair->first->data.insert(P->data.rbegin()->first->data.begin(), P->data.rbegin()->first->data.end());
		oapair->second->data.insert(P->data.rbegin()->second->data.begin(), P->data.rbegin()->second->data.end());
		oapair->second->data.insert(prefix->data.begin(), prefix->data.end());
		insertFrequentItemset(oapair, frequentClosedItemsets);
		cleanUpOAPair(oapair);
	}
}

bool isClosable(const OAPair & const oa_pair, FormalContext &local_fc){
	if(!checkOrder(&local_fc)){
		cout<<"Context elements order violated"<<endl;
		system("PAUSE");
	}
	list<const OAPair* const> deltaClosureProcessed;
	int offset = local_fc.data.size()-local_fc.processedNo;
	for(vector<OAPair>::const_reverse_iterator iter = local_fc.data.rbegin()+offset; iter != local_fc.data.rend(); ++iter){
		printOAPair(&*iter);
		if(iter->first->data.size()>oa_pair.first->data.size()){
			deltaClosureProcessed.push_back(&*iter);
		}
	}
	if(deltaClosureProcessed.size()==0){
		return true;
	}
	
	for(set<Object>::const_iterator itero = oa_pair.first->data.begin(); itero!=oa_pair.first->data.end(); ++itero){
		for(list<const OAPair* const>::iterator iteryb = deltaClosureProcessed.begin(); iteryb!=deltaClosureProcessed.end(); ){
			bool deltaClosureUnchanged=true;
			for(set<Object>::const_iterator itery = (*iteryb)->first->data.begin(); itery!=(*iteryb)->first->data.end(); ++itery){
				if(*itery<*itero){
					continue;
				}
				if((*iteryb)->first->data.find(*itero)==(*iteryb)->first->data.end()){
					deltaClosureProcessed.erase(iteryb++);
					deltaClosureUnchanged=false;
					break;
				}else{
					//nothing = skip o in Y
				}
			}
			if(deltaClosureUnchanged){
				++iteryb;
			}
		}
	}
	return(deltaClosureProcessed.size()==0);
}


void printFrequentClosedItemsets(const vector<const OAPair* const> * const fci, bool asChars, ostream &sink){
	sink<<endl;
	sink<<"FREQUENT CLOSED ITEMSETS:"<<endl;
	for(vector<const OAPair* const>::const_iterator iter = fci->begin(); iter!=fci->end(); ++iter){
		printOAPair(*iter, asChars, sink);
	}
}

void printOAPair(const OAPair * const oa, bool asChars, ostream &sink){
	ObjectSet *O = oa->first;
	AttributeSet *A = oa->second;
	sink<< "O:{ ";
	for (std::set<Object>::const_iterator i = O->data.begin(); i != O->data.end(); i++){
		sink<< *i << " ";
	}
	sink << "}, A:{ ";
	for (std::set<Attribute>::const_iterator i = A->data.begin(); i != A->data.end(); i++){
		if(asChars){
			sink<< (char)('a'-1+*i) << " ";
		}else{
			sink<<(*i) << " ";
		}
	}
	sink<< "}" <<endl;
}

void printFormalContext(const FormalContext * const fc, ostream &sink){
	sink<<"**FORMAL CONTEXT DUMP ***proc: " <<fc->processedNo<<" ******" << endl; 
	for(vector<OAPair>::const_iterator iter = fc->data.begin(); iter!=fc->data.end(); ++iter){
		printOAPair(&*iter);
	}
	sink<<""<<endl;
}

void cleanUpFormalContext(FormalContext* fc, bool removeObjects, bool removeAttributes){
	for(vector<OAPair>::iterator iter = fc->data.begin(); iter!=fc->data.end(); ++iter){
		if(removeObjects){
			delete iter->first;
		}if(removeAttributes){
			delete iter->second;
		}
	}
	delete fc;
}

void cleanUpOAPair(OAPair* oa){
	delete oa->first;
	delete oa->second;
	delete oa;
}

void insertFrequentItemset(const OAPair* const oa, vector<const OAPair* const> * const fci){
	for(vector<const OAPair* const>::const_iterator iter = fci->begin(); iter!=fci->end(); ++iter){
		if(*((*iter)->second)==*(oa->second)){
			return;
		}
	}
	fci->push_back(new pair<ObjectSet*, AttributeSet*>(new ObjectSet(*(oa->first)), new AttributeSet(*(oa->second))));
}

void cleanUpFrequentClosedItemsets(vector<const OAPair* const>* fci){
	for(vector<const OAPair* const>::iterator iter = fci->begin(); iter!=fci->end(); ){
		vector<const OAPair* const>::iterator eraser = iter++;
		OAPair* tmp = const_cast<OAPair*>(*eraser);
		cleanUpOAPair(tmp);
	}
	delete fci;
}

bool checkOrder(const FormalContext * const fc){
	//bool onFresh=false;
	bool onProcessed=true;
	for(vector<OAPair>::const_iterator iter = fc->data.begin(); iter!=fc->data.end(); ++iter ){
		if(iter->second->processed){
			if(onProcessed){
				continue;	
			}else{
				return false;
			}
		}else{
			if(onProcessed){
				onProcessed=false;
			}
		}
	}
	return true;
}